﻿//-------------------------------------
// WBFSSync - WBFSSync.exe
//
// Copyright 2009 Caian (ÔmΣga Frøst) <frost.omega@hotmail.com> :
//
// WBFSSync is Licensed under the terms of the Microsoft Reciprocal License (Ms-RL)
//
// CommandLine.cs:
//
// Classe estática que agrupa todas as rotinas dos comandos passados em "args" durante a
// inicialização do programa
//
//-------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Runtime.InteropServices;
using System.Threading;
using WBFSSync;
using System.IO;

namespace DevConsole
{
    static class Operations
    {
        //---------------------- Estáticas

        public static System.Timers.Timer Timer = null;
        public static Thread Thread = null;

        public static String FileIn = String.Empty;
        public static String FileOut = String.Empty;
        public static ProgressIndicator Progress = null;

        static Dictionary<int, String> ErrorCodes = new Dictionary<int, string>();

        //---------------------- Rotinas

        public static void PrintToConsole(Object sender, EventArgs e)
        {
            Console.Clear();
            Console.WriteLine(String.Format("{0}\n{1}\n{2}", FileIn, FileOut, Progress.Ratio.ToString("P02")));
        }

        public static string FormatErrorCode(int error)
        {
            if (ErrorCodes.Count == 0)
            {
                #region Error Codes
                //Códigos de erro para WBFSDevice
                ErrorCodes.Add(-1, "Fail");
                ErrorCodes.Add(-2, "Operation aborted");
                ErrorCodes.Add(-3, "Incorrect identification code for the partition(Magic) {1}");
                ErrorCodes.Add(-4, "Saved sector size mismatch from partition info '{1}'");
                ErrorCodes.Add(-5, "Saved total sectors count mismatch from partition info '{1}'");
                ErrorCodes.Add(-6, "Access denied");
                ErrorCodes.Add(-7, "There is no suficient blocks to continue the operation");
                ErrorCodes.Add(-8, "The disc is not a valid Wii disc");
                ErrorCodes.Add(-9, "Disc not found");
                ErrorCodes.Add(-10, "A disc with the same name or code already exists");
                ErrorCodes.Add(-11, "Critical error");
                ErrorCodes.Add(-12, "Windows native error");
                ErrorCodes.Add(-13, "Invalid argument");
                ErrorCodes.Add(-14, "The disc's name is too large and will be trimmed");
                ErrorCodes.Add(-15, "The disc's code is too large and will be trimmed");
                ErrorCodes.Add(-16, "The disc's code is too small and will be filled with '0'");

                //Códigos de erro para IOManager
                ErrorCodes.Add(-1001, "Fail");
                ErrorCodes.Add(-1002, "An assynchronous operation is already running");
                ErrorCodes.Add(-1003, "Invalid handle");
                ErrorCodes.Add(-1004, "The handle has been closed");
                ErrorCodes.Add(-1005, "Invalid argument '{1}'");
                ErrorCodes.Add(-1006, "The context is closed and can't be used");
                ErrorCodes.Add(-1007, "The handle is in use by another context");
                ErrorCodes.Add(-1008, "The context already locked this handle");
                ErrorCodes.Add(-1009, "The context didn't lock the handle");
                ErrorCodes.Add(-1010, "Windows native error  '{1}'");
                ErrorCodes.Add(-1011, "Access denied");
                ErrorCodes.Add(-1012, "Error during Seek operation");
                ErrorCodes.Add(-1013, "Error during Read operation");
                ErrorCodes.Add(-1014, "Error during Write operation");
                ErrorCodes.Add(-1015, "Error during Set EOF operation");
                ErrorCodes.Add(-1016, "Error during Handle closing");
                ErrorCodes.Add(-1017, "Error, the handle is still open");

                //Códigos de erro para ScrubDisc
                ErrorCodes.Add(-2001, "SyncScrub identification code is incorrect");
                ErrorCodes.Add(-2002, "Version not supported by the application");
                ErrorCodes.Add(-2003, "Inconsistent data found in the disc");

                ErrorCodes.Add(-2004, "Invalid argument");
                ErrorCodes.Add(-2005, "Fail");
                ErrorCodes.Add(-2006, "Operation aborted");
                #endregion
            }

            if (ErrorCodes.ContainsKey(error)) 
                return ErrorCodes[error];
            else 
                return error.ToString();
        }

        public static void StartTimer()
        {
            Progress = new ProgressIndicator();

            Timer = new System.Timers.Timer();
            Timer.Elapsed += new ElapsedEventHandler(PrintToConsole);
            Timer.Interval = 1000; //1s
            Timer.Start();
        }

        public static void StopTimer()
        {
            Console.Clear();
            Timer.Stop();
            Timer = null;
        }

        //---------------------- IO

        //----------------------
        public static void CommList(String[] args)
        {
            if ((args.Length != 2) && (args.Length != 3))
            {
                Console.WriteLine("usage: listgames sourcedrive savelist");
                Console.WriteLine("sourcedrive: source wbfs drive in format 'X:\\' or just 'X'");
                Console.WriteLine("savelist: optional, a list to be created with the games of the wbfs drive");
                return;
            }

            String drive = "";
            if (args[1].Length > 3) //Arquivo
            {
                drive = args[1];
            }
            else
            {
                drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            }

            WBFSDevice device = new WBFSDevice();
            int r = device.Open(drive, false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            StringBuilder list = new StringBuilder(device.DiscCount);
            IDisc disc = null;
            int i = 0;

            list.AppendLine(device.DiscCount + " Games");

            while ((disc = device.GetDiscByIndex(i++)) != null)
            {
                //Trata o digit grouping
                string size = disc.WBFSSize.ToString();
                for (int j = size.Length - 3; j > 0; j -= 3) size = size.Insert(j, ",");

                list.AppendLine(String.Format("{0,-6} - {1,-32} - {2,13} {3,2}", disc.Code, disc.Name, size,
                    disc.IsDoubleLayer ? "DL" : "  "));
            }

            device.Close();

            Console.WriteLine(list.ToString());

            if (args.Length == 3)
            {
                try
                {
                    File.WriteAllText(args[2], list.ToString(), Encoding.Default);
                }
                catch
                {
                    Console.WriteLine("ERROR");
                    Console.WriteLine("Error writing list file");
                }
            }
        }

        //----------------------
        public static void CommAdd(String[] args)
        {
            if (args.Length != 3)
            {
                Console.WriteLine("usage: add destdrive filein");
                Console.WriteLine("destdrive: destination wbfs drive in format 'X:\\' or just 'X'");
                Console.WriteLine("filein: source file, iso or syncscrub");
                return;
            }

            if (!File.Exists(args[2]))
            {
                Console.WriteLine("ERROR");
                Console.WriteLine("File not found");
                return;
            }

            FileIn = args[2];
            FileOut = args[1];

            String drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            WBFSDevice device = new WBFSDevice();
            int r = device.Open(drive, false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            if (ScrubDisc.IsScrubDisc(args[2]) == 0)
            {
                ScrubDisc disc = new ScrubDisc();
                r = disc.Open(args[2], false);
                if (r != 0)
                {
                    device.Close();
                    Console.WriteLine("ERROR");
                    Console.WriteLine(FormatErrorCode(r));
                    return;
                }

                StartTimer();
                r = device.AddDisc(disc, Progress, false);
                StopTimer();
            }
            else
            {
                StartTimer();
                r = device.AddDisc(args[2], Progress, false);
                StopTimer();
            }

            device.Close();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }
        }

        //----------------------
        public static void CommExtract(String[] args)
        {
            if ((args.Length != 4) && (args.Length != 5))
            {
                Console.WriteLine("usage: extract sourcedrive gamecode fileout -s");
                Console.WriteLine("sourcedrive: source wbfs drive in format 'X:\\' or just 'X'");
                Console.WriteLine("gamecode: the 6 letter game code from the game in the wbfs drive");
                Console.WriteLine("fileout: destination file, output will be an iso or a syncscrub");
                Console.WriteLine("-s: optional, extract to syncscrub instead of iso");
                return;
            }

            String drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            WBFSDevice device = new WBFSDevice();
            int r = device.Open(drive, false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            Boolean extractAsSyncScrub = (args.Length == 5) && (String.Compare(args[4], "-s", true) == 0);

            if (extractAsSyncScrub)
            {
                if (!args[3].EndsWith(ScrubDisc.ext_wiiscrub)) args[3] += ScrubDisc.ext_wiiscrub;
            }
            else
            {
                if (!args[3].EndsWith(WiiDisc.ext_wiiiso)) args[3] += WiiDisc.ext_wiiiso;
            }

            FileIn = drive + args[2];
            FileOut = args[3];

            IDisc disc = device.GetDiscByCode(args[2]);
            if (disc == null)
            {
                device.Close();
                Console.WriteLine("ERROR");
                Console.WriteLine("Disc not found!");
                return;
            }

            if (extractAsSyncScrub)
            {
                StartTimer();
                r = ScrubDisc.CreateScrub(disc, args[3], Progress);
                StopTimer();
            }
            else
            {
                StartTimer();
                r = device.ExtractDisc(args[3], disc, Progress);
                StopTimer();
            }

            device.Close();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));

                if (File.Exists(args[3])) File.Delete(args[3]);
                return;
            }
        }

        //----------------------
        public static void CommScrub(String[] args)
        {
            if ((args.Length != 3) && (args.Length != 4))
            {
                Console.WriteLine("usage: scrub filein fileout -psX");
                Console.WriteLine("filein: source file, must be an iso");
                Console.WriteLine("fileout: destination file, output will be a syncscrub");
                Console.WriteLine("-psX: optional, Partition Selection, can be:");
                Console.WriteLine("-ps0: default, OnlyGamePartition");
                Console.WriteLine("-ps1: RemoveUpdatePartition");
                Console.WriteLine("-ps2: AllPartitions");
                return;
            }

            PartitionSelection ps = PartitionSelection.OnlyGamePartition;
            if (args.Length == 4)
            {
                switch (args[3])
                {
                    default:
                    case "-ps0": ps = PartitionSelection.OnlyGamePartition; break;
                    case "-ps1": ps = PartitionSelection.RemoveUpdatePartition; break;
                    case "-ps2": ps = PartitionSelection.AllPartitions; break;
                }
            }

            if (!File.Exists(args[1]))
            {
                Console.WriteLine("ERROR");
                Console.WriteLine("File not found");
                return;
            }

            if (!args[2].EndsWith(ScrubDisc.ext_wiiscrub)) args[2] += ScrubDisc.ext_wiiscrub;

            FileIn = args[1];
            FileOut = args[2];

            StartTimer();
            int r = ScrubDisc.CreateScrub(args[1], args[2], Progress, ps);
            StopTimer();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));

                if (File.Exists(args[2])) File.Delete(args[2]);
                return;
            }
        }

        //----------------------
        public static void CommUnscrub(String[] args)
        {
            if (args.Length != 3)
            {
                Console.WriteLine("usage: unscrub filein fileout");
                Console.WriteLine("filein: source file, must be a syncscrub");
                Console.WriteLine("fileout: destination file, output will be an iso");
                return;
            }

            if (!File.Exists(args[1]))
            {
                Console.WriteLine("ERROR");
                Console.WriteLine("File not found");
                return;
            }

            if (!args[2].EndsWith(WiiDisc.ext_wiiiso)) args[2] += WiiDisc.ext_wiiiso;

            FileIn = args[1];
            FileOut = args[2];

            ScrubDisc disc = new ScrubDisc();
            int r = disc.Open(args[1], false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            StartTimer();
            r = disc.Extract(args[2], Progress);
            StopTimer();

            disc.Close();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));

                if (File.Exists(args[2])) File.Delete(args[2]);
                return;
            }
        }

        //----------------------
        public static void CommUpdateScrub(String[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("usage: updatescrub filein");
                Console.WriteLine("filein: source file, must be a syncscrub");
                return;
            }

            if (!File.Exists(args[1]))
            {
                Console.WriteLine("ERROR");
                Console.WriteLine("File not found");
                return;
            }

            int r = ScrubDisc.UpdateScrub(args[1]);

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }
        }

        //----------------------
        public static void CommUnCIso(String[] args)
        {
            if ((args.Length != 3) && (args.Length != 4))
            {
                Console.WriteLine("usage: unciso filein fileout -s");
                Console.WriteLine("filein: source file, must be a cISO");
                Console.WriteLine("fileout: destination file, output will be an iso");
                Console.WriteLine("-s: optional, extract to syncscrub instead of iso");
                return;
            }

            if (!File.Exists(args[1]))
            {
                Console.WriteLine("ERROR");
                Console.WriteLine("File not found");
                return;
            }

            Boolean extractAsSyncScrub = (args.Length == 4) && (String.Compare(args[3], "-s", true) == 0);

            if (extractAsSyncScrub)
            {
                if (!args[2].EndsWith(ScrubDisc.ext_wiiscrub)) args[2] += ScrubDisc.ext_wiiscrub;
            }
            else
            {
                if (!args[2].EndsWith(WiiDisc.ext_wiiiso)) args[2] += WiiDisc.ext_wiiiso;
            }

            FileIn = args[1];
            FileOut = args[2];

            int r = 0;

            if (extractAsSyncScrub)
            {
                StartTimer();
                r = ScrubDisc.CreateScrubFromCISO(args[1], args[2], Progress);
                StopTimer();
            }
            else
            {
                StartTimer();
                r = ScrubDisc.ExtractCISO(args[1], args[2], Progress);
                StopTimer();
            }

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));

                if (File.Exists(args[2])) File.Delete(args[2]);
                return;
            }
        }

        //----------------------
        public static void CommDelete(String[] args)
        {
            if (args.Length != 3)
            {
                Console.WriteLine("usage: delete sourcedrive gamecode");
                Console.WriteLine("sourcedrive: source wbfs drive in format 'X:\\' or just 'X'");
                Console.WriteLine("gamecode: the 6 letter game code from the game in the wbfs drive");
                return;
            }

            String drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            WBFSDevice device = new WBFSDevice();
            int r = device.Open(drive, false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            IDisc disc = device.GetDiscByCode(args[2]);
            if (disc == null)
            {
                device.Close();
                Console.WriteLine("ERROR");
                Console.WriteLine("Disc not found!");
                return;
            }

            r = disc.Delete();
            device.Close();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }
        }

        //----------------------
        public static void CommRename(String[] args)
        {
            if (args.Length != 5)
            {
                Console.WriteLine("usage: rename sourcedrive gamecode newcode newname");
                Console.WriteLine("sourcedrive: source wbfs drive in format 'X:\\' or just 'X'");
                Console.WriteLine("gamecode: the 6 letter game code from the game in the wbfs drive");
                Console.WriteLine("newcode: the new 6 letter game code to assign to the game");
                Console.WriteLine("newname: the new name to assign to the game, 32 characters max");
                return;
            }

            String drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            WBFSDevice device = new WBFSDevice();
            int r = device.Open(drive, false);
            if (r != 0)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }

            IDisc disc = device.GetDiscByCode(args[2]);
            if (disc == null)
            {
                device.Close();
                Console.WriteLine("ERROR");
                Console.WriteLine("Disc not found!");
                return;
            }

            r = device.ChangeDiscHeader(disc, args[4], args[3]);
            device.Close();

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
                return;
            }
        }

        //----------------------
        public static void CommFormat(String[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("usage: format sourcedrive");
                Console.WriteLine("sourcedrive: source drive in format 'X:\\' or just 'X'");
                return;
            }

            String drive = (args[1].Length == 3) && (args[1].EndsWith(":\\")) ? args[1] : (args[1][0] + ":\\");
            int r = WBFSDevice.Format(drive);

            if (r == 0)
            {
                Console.WriteLine("SUCCEEDED");
            }
            else
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(FormatErrorCode(r));
            }
        }
    }
}
